import Foundation

protocol DWJsonNumberValue {}
protocol DWJsonDecimalValue {}
extension NSNumber: DWJsonNumberValue {}
extension Decimal: DWJsonNumberValue, DWJsonDecimalValue {}
extension NSDecimalNumber: DWJsonDecimalValue {}

protocol DWJsonDigitValue: DWJsonNumberValue {
    init?(truncating: NSNumber)
}

protocol DWJsonFloatValue: DWJsonDigitValue { }
extension Double: DWJsonFloatValue {}
extension Float: DWJsonFloatValue {}
extension CGFloat: DWJsonFloatValue {}

extension Bool: DWJsonDigitValue {}

protocol DWJsonIntValue: DWJsonDigitValue { }
extension Int: DWJsonIntValue {}
extension Int64: DWJsonIntValue {}
extension Int32: DWJsonIntValue {}
extension Int16: DWJsonIntValue {}
extension Int8: DWJsonIntValue {}
extension UInt: DWJsonIntValue {}
extension UInt64: DWJsonIntValue {}
extension UInt32: DWJsonIntValue {}
extension UInt16: DWJsonIntValue {}
extension UInt8: DWJsonIntValue {}

func dw_number(_ value: Any, type: Any.Type) -> DWJsonNumberValue? {
    guard let str = dw_numberString(value) else { return nil }
    guard let decimal = Decimal(string: str) else { return nil }
    if let digitType = type as? DWJsonDigitValue.Type {
        return Double("\(decimal)").flatMap { NSNumber(value: $0) }.flatMap { digitType.init(truncating: $0) }
    }
    if type is NSDecimalNumber.Type {
        return NSDecimalNumber(decimal: decimal)
    }
    if type is Decimal.Type { return decimal }
    return Double("\(decimal)").flatMap { NSNumber(value: $0) }
}

private func dw_numberString(_ value: Any) -> String? {
    if let date = value as? Date {
        return "\(date.timeIntervalSince1970)"
    }
    let lower = "\(value)".lowercased()
    if lower == "true" || lower == "yes" { return "1" }
    if lower == "false" || lower == "no" { return "0" }
    return lower
}
